/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 /*
 * @State
 * 
 * @Tags Jetstream2
 */
class Benchmark {
    public static double[] GenerateFloatInput() {
        return new double[] { 1.0, 2.0 };
    }

    public static int[] GenerateBitOpsInput() {
        return new int[] { 2, 3 };
    }

    public static int[] GenerateIntegerInput() {
        return new int[] { 2, 4 };
    }

    public static int[] GenerateFakeRandomInteger() {
        int[] resource = new int[] { 12, 43, 56, 76, 89, 54, 45, 32, 35, 47, 46, 44, 21, 37, 84 };
        return resource;
    }

    public static double[] GenerateFakeRandomFloat() {
        double[] resource = new double[] { 12.2, 43.5, 56.2, 76.6, 89.7, 54.9, 45.2, 32.5, 35.6, 47.2, 46.6, 44.3, 21.2,
                37.6, 84.57 };
        return resource;
    }

    // 浮点计算
    public static void FloatNumAddition() {
        int count = 3000000;
		double[] floatInputs = GenerateFloatInput();
        double f1 = 1.0;
        double f2 = 2.0;
        double f3 = 1.0;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[(int) (i % f3) & (resourcesLength - 1)] & 1) == 0) {
                f3 += f1;
            } else {
                f3 += f2;
            }
        }
        double end = Timer.now();
        // Log.print("FloatNumAddition: " + f3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - FloatNumAddition:\t" + time + "\tms");
    }

    public static void FloatNumSubstraction() {
        int count = 3000000;
        double[] floatInputs = GenerateFloatInput();
        double f1 = 1.0;
        double f2 = 2.0;
        double f3 = 13000000.0;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[(int) (i % f3) & (resourcesLength - 1)] & 1) == 0) {
                f3 -= f1;
            } else {
                f3 -= f2;
            }
        }
        double end = Timer.now();
        Log.print("FloatNumSubstraction: " + f3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - FloatNumSubstraction:\t" + time + "\tms");
    }

    public static void FloatNumProduction() {
        int count = 3000000;
        double[] floatInputs = GenerateFloatInput();
        double f1 = 1.0;
        double f2 = 2.0;
        double f3 = 1.0;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[(int) i & (resourcesLength - 1)] & 1) == 0) {
                f3 += f3 * f1;
            } else {
                f3 += f3 * f2;
            }
        }
        double end = Timer.now();
        Log.print("FloatNumProduction: " + f3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - FloatNumProduction:\t" + time + "\tms");
    }

    public static void FloatNumDivision() {
        int count = 3000000;
        double f3 = 1.0;
        int[] resources = GenerateFakeRandomInteger();
        double[] inputs = GenerateFakeRandomFloat();
        double start = Timer.now();
        int resourcesLength = resources.length;
        int inputsLength = inputs.length;
        for (int i = 0; i < count; i++) {
            if ((resources[i & (resourcesLength - 1)] & 1) == 0) {
                f3 += f3 / inputs[i & (inputsLength - 1)] / 0.1;
            } else {
                f3 += f3 / inputs[(i + resources[i & (resourcesLength - 1)]) & (inputsLength - 1)] / 0.1;
            }
        }
        double end = Timer.now();
        Log.print("FloatNumDivision: " + f3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - FloatNumDivision:\t" + time + "\tms");
    }

    // 位运算
    public static void BitOpsAND() {
        int count = 30000000;
        int[] bitInputs = GenerateBitOpsInput();
        int b1 = 2;
        int b2 = 3;
        int b3 = 1;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[i % b3 & (resourcesLength - 1)] & 1) == 0) {
                b3 = (b3 & b1) + 1;
            } else {
                b3 = (b3 & b2) + 1;
            }
        }
        double end = Timer.now();
        Log.print("BitOpsAND: " + b3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - BitOpsAND:\t" + time + "\tms");
    }

    public static void BitOpsOR() {
        int count = 30000000;
        int b1 = 2;
        int b2 = 3;
        int b3 = 1;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[i % b3 & (resourcesLength - 1)] & 1) == 0) {
                b3 = (b3 | b1) + 1;
            } else {
                b3 = (b3 | b2) + 1;
            }
        }
        double end = Timer.now();
        Log.print("BitOpsOR: " + b3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - BitOpsOR:\t" + time + "\tms");
    }

    public static void BitOpsXOR() {
        int count = 30000000;
        int b3 = 1;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if (b3 <= 0) {
                b3 = 1;
            }
            if ((resources[i % b3 & (resourcesLength - 1)] & 1) == 0) {
                b3 = b3 ^ resources[i & (resourcesLength - 1)];
            } else {
                b3 = b3 ^ resources[(i + 5) & (resourcesLength - 1)];
            }
        }
        double end = Timer.now();
        Log.print("BitOpsXOR: " + b3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - BitOpsXOR:\t" + time + "\tms");
    }

    public static void BitOpsNOT() {
        int count = 30000000;
        int b1 = -2;
        int b2 = -3;
        int b3 = 1;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
          if ((resources[i % b3 & (resourcesLength - 1)] & 1) == 0) {
            b3 += ~b1;
          } else {
            b3 += ~b2;
          }
        }
        double end = Timer.now();
        Log.print("BitOpsNOT: " + b3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - BitOpsNOT:\t" + time + "\tms");
      }

    public static void BitOpsShiftLeft() {
        int count = 30000000;
        int[] bitInputs = GenerateBitOpsInput();
        int b1 = 2;
        int b3 = 1;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[i % b3 & (resourcesLength - 1)] & 1) == 0) {
                b3 += b1 << 1;
            } else {
                b3 += b1 << 2;
            }
        }
        double end = Timer.now();
        Log.print("BitOpsShiftLeft: " + b3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - BitOpsShiftLeft:\t" + time + "\tms");
    }

    public static void BitOpsShiftRight() {
        int count = 30000000;
        int[] bitInputs = GenerateBitOpsInput();
        int b1 = 2; 
        int b3 = 1;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[i % b3 & (resourcesLength - 1)] & 1) == 0) {
                b3 += b1 >> 1;
            } else {
                b3 += b1 >> 2;
            }
        }
        double end = Timer.now();
        Log.print("BitOpsShiftRight: " + b3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - BitOpsShiftRight:\t" + time + "\tms");
    }

    // 整数计算
    public static void IntegerNumAddition() {
        int count = 30000000;
        int[] IntegerInputs = GenerateIntegerInput();
        int[] resources = GenerateFakeRandomInteger();
        int i1 = 2;
        int i2 = 4;
        int i3 = resources[0];
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[i % i3 & (resourcesLength - 1)] & 1) == 0) {
                i3 += i1;
            } else {
                i3 += i2;
            }
        }
        double end = Timer.now();
        Log.print("IntegerNumAddition: " + i3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - IntegerNumAddition:\t" + time + "\tms");
    }

    public static void IntegerNumSubstraction() {
        int count = 30000000;
        int[] IntegerInputs = GenerateIntegerInput();
        int i1 = 2;
        int i2 = 4;
        int i3 = 130000000;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if ((resources[i % i3 & (resourcesLength - 1)] & 1) == 0) { // 1.07ms
                i3 -= i1;
            } else {
                i3 -= i2;
            }
        }
        double end = Timer.now();
        Log.print("IntegerNumSubstraction: " + i3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - IntegerNumSubstraction:\t" + time + "\tms");
    }

    public static void IntegerNumProduction() {
        int count = 30000000;
        int i3 = 1;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length - 1;
        for (int i = 0; i < count; i++) {
            if ((resources[i % i3 & resourcesLength] & 1) == 0) {
                i3 *= 3;
            } else {
                i3 *= 2;
            }
            if (i3 > 10000000) {
                i3 = 1;
            }
        }
        double end = Timer.now();
        Log.print("IntegerNumProduction: " + i3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - IntegerNumProduction:\t" + time + "\tms");
        
    }

    public static void IntegerNumDivision() {
        int count = 30000000;
        int i1 = 2;
        int i2 = 4;
        int i3 = 32768;
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            if (i3 <= 4) {
                i3 = 32768;
            }
            if ((resources[i % i3 & (resourcesLength - 1)] & 1) == 0) {
                i3 = i3 / i1;
            } else {
                i3 = i3 / i2;
            }

        }
        double end = Timer.now();
        Log.print("IntegerNumDivision: " + i3);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - IntegerNumDivision:\t" + time + "\tms");
    }

    // 浮点数比较
    public static void FloatNumComparision() {
        int count = 3000000; // input;
        double[] resources = GenerateFakeRandomFloat();
        int res = 1;
        double start = Timer.now();
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            int next_index = i + 5;
            int next_res = res + 1;
            if (resources[i % res & (resourcesLength - 1)] > resources[next_index % next_res & (resourcesLength - 1)]) {
                res += 1;
            } else {
                res += 2;
            }
        }
        double end = Timer.now();
        Log.print("FloatNumComparision: " + res);
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - FloatNumComparision:\t" + time + "\tms");
    }

    // String运算
    public static void StringCalculation() {
        int count = 3000000 / 1000;
        String str1 = "h";
        String res = "11";
        int[] resources = GenerateFakeRandomInteger();
        double start = Timer.now();
        for (int i = 0; i < count; i++) {
            if (resources[i % res.length() % 15] > resources[(i + resources[i % 15]) % res.length() % 15]) {
                res += str1 + i;
            } else {
                res += str1;
            }
        }
        double end = Timer.now();
        Log.print("StringCalculation: " + res.length());
        double time = (double) (end - start) / 1000000;
        System.out.println("Numerical Calculation - StringCalculation:\t" + time + "\tms");
    }

    public static void main(String[] args) throws Exception {
        runNumerical();
    }

    public static void runNumerical() {
        FloatNumAddition();
        FloatNumSubstraction();
        FloatNumProduction();
        FloatNumDivision();
        BitOpsAND();
        BitOpsOR();
        BitOpsXOR();
        BitOpsNOT();
        BitOpsShiftLeft();
        BitOpsShiftRight();
        IntegerNumAddition();
        IntegerNumSubstraction();
        IntegerNumProduction();
        IntegerNumDivision();
        FloatNumComparision();
        StringCalculation();
    }
}

class Timer {
    public static double now() {
        return System.nanoTime();
    }
}

class Log {
    static boolean debug = false;

   public static void print(String msg) {
        if (debug) {
            System.out.println(msg);
        }
    }

}